
package com.yadong.smarthome2.connections;

import java.util.List;

import com.yadong.smarthome2.device.Device;
import com.yadong.smarthome2.device.Device.DeviceConnectionType;
import com.yadong.smarthome2.messageparser.RequestManager;
import com.yadong.smarthome2.protocol2.ALog;
import com.yadong.smarthome2.protocol2.RequestProtocol.Request;
import com.yadong.smarthome2.protocol2.ResultProtocol.DeviceInfo.DeviceType;
import com.yadong.smarthome2.protocol2.ResultProtocol.DeviceInfo.Status;
import com.yadong.smarthome2.protocol2.ResultProtocol.Function;
import com.yadong.smarthome2.protocol2.ResultProtocol.Result;

public abstract class Connection extends Thread {
    public enum ConnectionState {
        DISCONNECTED, WORKING, IDEL
    }

    public interface ConnectionListener {
        public void onConnectionFaild(Connection connection);
        public void onConnectionDisconnected(Connection connection);
        public void onConnectionConnected(Connection connection);
        public void onDeviceInitialized(Connection connection);
    }

    private Device mDevice;

    private ConnectionListener mConnectionListener;
    private volatile boolean mStopRunning = false;
    private ConnectionState mConnectionState = ConnectionState.DISCONNECTED;
    private MessageSendThread mMessageSendThread;
    private RequestManager mRequestManager;
    private ResultListener mResultListener;
    private long mLastActive = System.currentTimeMillis();

    protected void doSend(Runnable runnable) {
        mMessageSendThread.addWork(runnable);
    }

    public Connection(Device device, ConnectionListener connectionListener,
            MessageSendThread sendThread, RequestManager requestManager,
            ResultListener resultListener) {
        this.mDevice = device;
        this.mConnectionListener = connectionListener;
        this.mMessageSendThread = sendThread;
        this.mRequestManager = requestManager;
        this.mResultListener= resultListener;
    }

    protected abstract boolean connectDevice(Device device);

    protected abstract void queyDeviceFunctions();

    protected abstract Result doLoop() throws Exception;

    protected abstract boolean sendDataInternal(Request request);

    protected abstract void forceStopConnection();

    public long getLastActive() {
        return mLastActive;
    }

    public DeviceConnectionType getDeviceType() {
        return mDevice.getDeviceConnectionType();
    }

    public ConnectionState getConnectionState() {
        return mConnectionState;
    }

    public Device getDevice() {
        return mDevice;
    }

    public void sendData(Request request) {
        ALog.logd(this, "..send data:" + request.getTag());
        boolean success = sendDataInternal(request);
        if (success) {
            // when send data success, update the update time.
            mLastActive = System.currentTimeMillis();
            mRequestManager.addRequest(getDevice(),request);
        }
    }

    public void forceStop() {
        mStopRunning = true;
        mConnectionState = ConnectionState.DISCONNECTED;
        forceStopConnection();
    }

    public void stopRunning() {
        mStopRunning = true;
    }

    protected void initDevice(DeviceType deviceType, int deviceInfoVersion,
            List<Function> functions, List<Status> status) {
        mDevice.init(deviceType, deviceInfoVersion, functions, status);
        if (mConnectionListener != null) {
            mConnectionListener.onDeviceInitialized(this);
        }
    }

    public void run() {
        if (connectDevice(mDevice)) {
            mConnectionState = ConnectionState.WORKING;
            if (mConnectionListener != null) {
                mConnectionListener.onConnectionConnected(this);
            }
            queyDeviceFunctions();

            while (!mStopRunning) {
                try {
                    Result message = doLoop();
                    if (message == null) {
                        continue;
                    }
                    // modify the active time
                    mLastActive = System.currentTimeMillis();
                    if (getDevice().getDeviceType() != DeviceType.CONTROL &&
                            !mRequestManager.pairRequest(mDevice, message)) {
                        ALog.loge(Connection.this, "no request pair the result");
                    }
                    if (mResultListener != null) {
                        mResultListener.onNewMessage(getDevice(), message);
                    }
                    ALog.logd(Connection.this, "receive message." + message);
                } catch (Exception e) {
                    e.printStackTrace();
                    ALog.loge(Connection.this, "eror....:" + e.getMessage());
                    forceStop();
                }
            }

            forceStop();

            if (mConnectionListener != null) {
                mConnectionState = ConnectionState.DISCONNECTED;
                mConnectionListener.onConnectionDisconnected(this);
            }
        } else {
            mConnectionState = ConnectionState.DISCONNECTED;
            if (mConnectionListener != null) {
                mConnectionListener.onConnectionFaild(this);
            }
        }
    }

}
